package org.flashIso.engine.core {
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.getQualifiedClassName;
	
	import org.flashIso.engine.base.IsoPerspective;
	import org.flashIso.engine.events.IsoEvent;
	import org.flashIso.engine.objects.IsoImage;
	
	[Exclude(name="addChildAt", kind="method")]
	[Exclude(name="removeChild", kind="method")]
	[Exclude(name="removeChildAt", kind="method")]
	[Exclude(name="setChildIndex", kind="method")]
	[Exclude(name="swapChildren", kind="method")]
	[Exclude(name="swapChildrenAt", kind="method")]
	
	[Event(name="positionChange", type="org.flashIso.engine.events.IsoEvent")]
	[Event(name="draw", type="org.flashIso.engine.events.IsoEvent")]
	
	/**
	 * Base class for all isometric elements
	 **/
	public class IsoObjectBase extends ValidateableSprite {
		private var _position : Point3D = new Point3D();
		private var _size : Point3D = new Point3D(50, 50, 50);		
		private var _delta : Point3D = new Point3D();
		
		private var _fills : FillList = new FillList();
		
		private var _drawShape : Boolean = true;
		
		private var _shape : Sprite;
		
		private var _drawHeight : Boolean = false;
		
		private var _rotation : Number = 0;
		private var _useImageFramesWhenRotating : Boolean = false;
		private var _image : IsoImage = new IsoImage();
		private var _hitAreaType : String;
		
		private var _customProperties : Object = new Object();
		
		private var _content:Sprite;
		
		private var _container:Sprite = new Sprite();
		
		
		public function IsoObjectBase() {
			super();
			_fills.addEventListener(Event.CHANGE, triggerValidation, false, 0, true);
			_delta.addEventListener(Event.CHANGE, triggerValidation, false, 0, true);
			
			_size.addEventListener(Event.CHANGE, sizeChangeHandler, false, 0, true);
			_position.addEventListener(Event.CHANGE, positionChangeHandler, false, 0, true);			

			mouseChildren = false;
			
			addChild(_container);
			
			_container.addChild(_image);
			_container.name = "container";
			_image.addEventListener(Event.CHANGE, imageChange, false, 0, true);
			
		}
		
		protected function positionChangeHandler(e:Event) : void {
			dispatchEvent(new IsoEvent(IsoEvent.POSITION_CHANGE));
		}
		protected function sizeChangeHandler(e:Event = null) : void {
			dispatchEvent(new IsoEvent(IsoEvent.SIZE_CHANGE));
			triggerValidation();
		}
		
		/**
		 * Size
		 **/
		public function get size() : Point3D {
			return _size;
		}
		public function set size(point3D : Point3D) : void {
			if (!point3D.equals(_size)){
				_size.autoValidation = false;
				_size.x = point3D.x;
				_size.y = point3D.y;
				_size.z = point3D.z;
				_size.autoValidation = true;
				sizeChangeHandler();
				triggerValidation();				
			}
		}
		
		/**
		 * CustomProperties object
		 **/
		public function get customProperties() : Object{
			return _customProperties;
		}
		
		public function set customProperties(obj:Object) : void{
			_customProperties = obj;
		}
		
		public function get content() : Sprite {
			if (_content == null){
				_content = new Sprite();
				_content.name = "content";
				_container.addChild(_content);
			}
			return _content;
		}
		
		public function getProperty(property:String) : String{
			return _customProperties[property];
		}
		public function get image() : IsoImage {
			return _image;
		}
		public function set image(value:IsoImage) : void  {
			if (_image != value){
				var level:int = numChildren;
				if (_image != null) {
					level = _container.getChildIndex(_image);
					_container.removeChild(_image);
					_image.removeEventListener(Event.CHANGE, imageChange);
				}
				_image = value;
				if (_image != null) {
					_container.addChildAt(_image, level);
					_image.addEventListener(Event.CHANGE, imageChange, false, 0, true);
				}
				imageChange();
			}
		}
		private function imageChange(e:Event = null) : void {
			dispatchEvent(new IsoEvent(IsoEvent.DRAW));
		}
		
		
		public function get fills() : FillList{
			return _fills;
		}
		
		public function set fills(list:FillList) : void  {
			_fills = list;
		}
		
		/**
		 * Flag indicating wheather the shape should be drawn
		 **/
		public function set drawShape(value : Boolean) : void {
			if (_drawShape != value) {
				_drawShape = value;
				triggerValidation();
			}
		}
		
		public function get drawShape() : Boolean {
			return _drawShape;
		}
		
		/**
		 * <p>Setter for hitArea type.</p>
		 * <p>Accepted values are: shapeAsMask, shapeAsHitArea, none</p>
		 */
		public function set hitAreaType(value : String) : void {
			if (_hitAreaType != value) {
				_hitAreaType = value;
				triggerValidation();
			}
		}
		public function get hitAreaType() : String {
			return _hitAreaType;
		}
		
		private function setHitAreaType() : void {
			switch((_hitAreaType+"").toLowerCase()) {
				case HitAreaType.SHAPE_AS_MASK.toLowerCase():
					_container.mask = _shape;
					this.hitArea = null;
					break;
				case HitAreaType.SHAPE_AS_HIT_AREA.toLowerCase():
					_container.mask = null;
					this.hitArea = _shape;
					break;
				default:
					_container.mask = null;
					this.hitArea = null;
					break;
			}
		}
		
		/**
		 * Flag for height draw.
		 * This should be use only for development purposes.
		 **/
		public function set drawHeight(value : Boolean) : void {
			if (_drawHeight != value) {
				if (_drawHeight){
					_position.removeEventListener(Event.CHANGE, triggerValidation);
				}
				_drawHeight = value;
				if (_drawHeight){
					_position.addEventListener(Event.CHANGE, triggerValidation, false, 0, true);
				}
				triggerValidation();
			}
		}
		
		public function get drawHeight() : Boolean {
			return _drawHeight;
		}
		
		/**
		 * 3D possition. When the object a child of an IsoMap, changing 
		 * position object determines changing the x,y sprite coordinates
		 **/
		public function get position() : Point3D {
			return _position;
		}
		public function set position(point : Point3D) : void {
			if (!_position.equals(point)) {
				_position.x = point.x;
				_position.y = point.y;
				_position.z = point.z;
			}
		}
		
		
		/**
		 * Use this object to move the sprite's origin point.
		 * In order to avoid tiles to overlap objects, 
		 * set the origin point to upper corner for tiles 
		 * and to down corner for objects.
		 *  
		 * 
		 **/
		public function get delta() : Point3D {
			return _delta;
		}
		public function set delta(point : Point3D) : void {
			if (!_delta.equals(point)) {
				_delta.x = point.x;
				_delta.y = point.y;
				_delta.z = point.z;
				triggerValidation();
			}
		}
		override public function set rotation (value:Number) : void {
			if (_rotation != value){
				_rotation = value;
				while (_rotation < 0){
					_rotation += 360;
				}
				while (_rotation > 360){
					_rotation -= 360;
				}
				triggerValidation();
				setImageFrame();
			}
		}
		
		
		/**
		 * 
		 * //to do:  a better explanation
		 * 
		 * If the loaded object is a swf, it's frame can be set here.
		 * 
		 * Ignored if image object doesn't contain a swf file loaded.
		 **/
		private function setImageFrame() : void {
			if (_image.loader != null && _image.loader.content is MovieClip){
				if (_useImageFramesWhenRotating){
					var n:uint = (_image.loader.content as MovieClip).totalFrames;
					var frame:Number = (Math.floor(((_rotation * n) / 360) + 0.5) % n) + 1;
					(_image.loader.content as MovieClip).gotoAndStop(frame);
				}else{
					
				}
			}
		}
		
		override public function get rotation() : Number{
			return _rotation;
		}
		
		
		/**
		 * <p>Flag indicating if the loaded swf IsoImage</p> 
		 * should circle through image frames while rotating the object.
		 * 
		 * <p>Ignored if the image doesn't contain a swf file loaded.</p>
		 **/
		public function set useImageFramesWhenRotating (value:Boolean) : void {
			if (_useImageFramesWhenRotating != value){
				_useImageFramesWhenRotating = value;
			}
		}
		
		public function get useImageFramesWhenRotating() : Boolean{
			return _useImageFramesWhenRotating;
		}
		
		public function draw(perspective : IsoPerspective) : void {
			
			if (_shape != null) {
				_shape.graphics.clear();
			}
			var p2d : Point2D;
			var p3d : Point3D;
			graphics.clear();
			if (_drawHeight){
				graphics.lineStyle(1, 0xff0000, 1);
				graphics.beginFill(0xff0000, 0.2);
				p2d = perspective.getProjection(new Point3D(8,2,-_position.z));
				graphics.moveTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-8,2,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-8,-2,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(8,-2,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				graphics.endFill();
				
				graphics.beginFill(0xff0000, 0.2);
				p2d = perspective.getProjection(new Point3D(2,8,-_position.z));
				graphics.moveTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-2,8,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-2,-8,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(2,-8,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				graphics.endFill();
				
				graphics.beginFill(0xff0000, 0.2);
				p2d = perspective.getProjection(new Point3D(8,2,0));
				graphics.moveTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-8,2,0));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-8,-2,0));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(8,-2,0));
				graphics.lineTo(p2d.x, p2d.y);
				graphics.endFill();
				
				graphics.beginFill(0xff0000, 0.2);
				p2d = perspective.getProjection(new Point3D(2,8,0));
				graphics.moveTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-2,8,0));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(-2,-8,0));
				graphics.lineTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(2,-8,0));
				graphics.lineTo(p2d.x, p2d.y);
				graphics.endFill();
				
				graphics.lineStyle(2, 0xff0000, 1);
				p2d = perspective.getProjection(new Point3D(0,0,0));
				graphics.moveTo(p2d.x, p2d.y);
				p2d = perspective.getProjection(new Point3D(0,0,-_position.z));
				graphics.lineTo(p2d.x, p2d.y);
				
			}
			
			if (_drawShape) {
//				trace("------- draw ------");
				if (_shape == null){
					_shape = new Sprite();
					_shape.name = "shape";
					addChild(_shape);
				}
				_shape.graphics.clear();
				var i : uint;
				for (i = 0; i < _fills.length; i++) {
//					trace("------- new fill ------");
					var fill : FillDescriptor = _fills.getItemAt(i);
					var j : uint;
					
					_shape.graphics.lineStyle(fill.lineThickness, fill.lineColor, fill.lineAlpha);
					_shape.graphics.beginFill(fill.color, fill.alpha);
					
					p3d = fill.pointsList.getPointAt(fill.pointsList.length - 1).clone(_delta.x, _delta.y, _delta.z);
					if (_rotation != 0){
						p3d = RotationMatrix.rotatePoint(p3d, _rotation);
					}
					p2d = perspective.getProjection(p3d);
					_shape.graphics.moveTo(p2d.x, p2d.y);
//					trace("moveTo: " + p2d.toString());
					for (j = 0;j < fill.pointsList.length;j++) {
						p3d = fill.pointsList.getPointAt(j).clone(_delta.x, _delta.y, _delta.z);
						if (_rotation != 0){
							p3d = RotationMatrix.rotatePoint(p3d, _rotation);
						}
						p2d = perspective.getProjection(p3d);
//						trace("timeTo: " + p2d.toString());
						_shape.graphics.lineTo(p2d.x, p2d.y);
					}
					_shape.graphics.endFill();
				}
			}else{
				if (_shape != null){
					removeChild(_shape);
					_shape = null;
				}
			}
			setHitAreaType();
			dispatchEvent(new IsoEvent(IsoEvent.DRAW));
		}
	}
}
